// ball_fileobserver2.h                                               -*-C++-*-

#ifndef INCLUDED_BALL_FILEOBSERVER2
#define INCLUDED_BALL_FILEOBSERVER2

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a thread-safe observer that emits log records to a file.
//
//@CLASSES:
//  ball::FileObserver2: observer that outputs log records to a file
//
//@SEE_ALSO: ball_record, ball_context, ball_observer,
//           ball_recordstringformatter
//
//@DESCRIPTION: This component provides a concrete implementation of the
// `ball::Observer` protocol, `ball::FileObserver2`, for publishing log records
// to a user-specified file.  The following inheritance hierarchy diagram shows
// the classes involved and their methods:
// ```
//               ,-------------------.
//              ( ball::FileObserver2 )
//               `-------------------'
//                        |              ctor
//                        |              disableFileLogging
//                        |              disableTimeIntervalRotation
//                        |              disableSizeRotation
//                        |              disablePublishInLocalTime
//                        |              enableFileLogging
//                        |              enablePublishInLocalTime
//                        |              forceRotation
//                        |              rotateOnSize
//                        |              rotateOnTimeInterval
//                        |              setLogFileFunctor
//                        |              setOnFileRotationCallback
//                        |              suppressUniqueFileNameOnRotation
//                        |              isFileLoggingEnabled
//                        |              isPublishInLocalTimeEnabled
//                        |              isSuppressUniqueFileNameOnRotation
//                        |              rotationLifetime
//                        |              rotationSize
//                        V
//                 ,--------------.
//                ( ball::Observer )
//                 `--------------'
//                                       dtor
//                                       publish
//                                       releaseRecords
// ```
// A `ball::FileObserver2` object processes the log records received through
// its `publish` method by writing them to a user-specified file.  Note that
// the `enableFileLogging` method must be called to enable logging since
// logging to a file is initially disabled following construction.  The format
// of published log records is user-configurable (see {Log Record Formatting}
// below).  In addition, a file observer may be configured to perform automatic
// log file rotation (see {Log File Rotation} below).
//
///File Observer Configuration Synopsis
///------------------------------------
// `ball::FileObserver2` offers several manipulators that may be used to
// configure various aspects of a file observer object.  These are summarized
// in the following table along with the accessors that can be used to query
// the current state of the configuration.  Further details are provided in the
// following sections and the function-level documentation.
// ```
// +-------------+------------------------------------+
// | Aspect      | Related Methods                    |
// +=============+====================================+
// | Log Record  | setLogFileFunctor                  |
// | Formatting  |                                    |
// +-------------+------------------------------------+
// | Log Record  | enablePublishInLocalTime           |
// | Timestamps  | disablePublishInLocalTime          |
// |             | isPublishInLocalTimeEnabled        |
// +-------------+------------------------------------+
// | File        | enableFileLogging                  |
// | Logging     | disableFileLogging                 |
// |             | isFileLoggingEnabled               |
// +-------------+------------------------------------+
// | Log File    | rotateOnSize                       |
// | Rotation    | rotateOnTimeInterval               |
// |             | disableSizeRotation                |
// |             | disableTimeIntervalRotation        |
// |             | setOnFileRotationCallback          |
// |             | suppressUniqueFileNameOnRotation   |
// |             | rotationSize                       |
// |             | rotationLifetime                   |
// |             | isSuppressUniqueFileNameOnRotation |
// +-------------+------------------------------------+
// ```
// In general, a `ball::FileObserver2` object can be dynamically configured
// throughout its lifetime (in particular, before or after being registered
// with a logger manager).  However, note that for `ball::FileObserver2`,
// configuration changes that affect how records are logged (e.g.,
// `enablePublishInLocalTime` and `disablePublishInLocalTime`) impact only
// those records that are published subsequent to making the configuration
// change.
//
///Log Record Formatting
///---------------------
// By default, the output format of published log records is:
// ```
// DATE_TIME PID:THREAD-ID SEVERITY FILE:LINE CATEGORY MESSAGE USER-FIELDS
// ```
// where `DATE` and `TIME` are of the form `DDMonYYYY` and `HH:MM:SS.mmm`,
// respectively (`Mon` being the 3-letter abbreviation for the month).  For
// example, assuming that no user-defined fields are present, a log record
// will have the following appearance when the default format is in effect:
// ```
// 18MAY2005_18:58:12.076 7959:1 WARN ball_fileobserver2.t.cpp:404 TEST hello!
// ```
// The default format can be overridden by supplying a suitable formatting
// functor to `setLogFileFunctor`.  For example, an instance of
// `ball::RecordStringFormatter` conveniently provides such a functor:
// ```
// fileObserver.setLogFileFunctor(
//             ball::RecordStringFormatter("\n%I %p:%t %s %f:%l %c %m %u\n"));
// ```
// The above statement will cause subsequent records to be logged in a format
// that is almost identical to the default format except that the timestamp
// attribute will be written in ISO 8601 format.  See
// {`ball_recordstringformatter`} for information on how format specifications
// are defined and interpreted.
//
// Note that the observer emits newline characters at the beginning and at the
// end of a log record by default, so the user needs to add them explicitly to
// the format string to preserve this behavior.
//
// Also note that in the sample message above the timestamp has millisecond
// precision (`18MAY2005_18:58:12.076`).  If microsecond precision is desired
// instead, consider using either the `%D` or `%O` format specification
// supported by `ball_recordstringformatter`.
//
///Log Record Timestamps
///---------------------
// By default, the timestamp attributes of published records are written in UTC
// time.  To write timestamps in local time instead, call the
// `enablePublishInLocalTime` method.  Note that the local time offset is
// calculated using the UTC timestamp of each record.  To revert to UTC time,
// call the `disablePublishInLocalTime` method.  Whether UTC time or local time
// is in effect can be queried via `isPublishInLocalTimeEnabled`.  However,
// note that if the user installs a log record formatting functor via
// `setLogFileFunctor`, then the supplied functor determines how record
// timestamps are rendered to the log.
//
///Local Time Offset Calculations
/// - - - - - - - - - - - - - - -
// The calculation of the local time offset adds some overhead to the
// publication of each log record.  If this overhead is an issue, it can be
// mitigated by installing a high-performance local-time offset callback for
// `bdlt::CurrentTime` in `main`.  See {`bsls_systemtime`} for the details of
// installing such a callback and see {`baltzo_localtimeoffsetutil`} for an
// example facility.  Note that such callbacks can improve performance for all
// users of `bdlt::CurrentTime`, not just the `ball` logger.
//
///Log Filename Patterns
///---------------------
// The `enableFileLogging` method supports the use of `%`-escape sequences to
// specify log filenames.  The recognized sequences are as follows:
// ```
// %Y - current year   (4 digits with leading zeros)
// %M - current month  (2 digits with leading zeros)
// %D - current day    (2 digits with leading zeros)
// %h - current hour   (2 digits with leading zeros)
// %m - current minute (2 digits with leading zeros)
// %s - current second (2 digits with leading zeros)
// %T - current datetime, equivalent to "%Y%M%D_%h%m%s"
// %p - process ID
// ```
// The date and time elements of the derived filename are based on the time
// when the log file is created.  Furthermore, these elements are based on
// either UTC time or local time depending on the value returned by
// `isPublishInLocalTimeEnabled`.  (See {Log Record Timestamps} for the
// similarity with the treatment of record timestamps.)
//
// For example, a log filename pattern of "task.log.%Y%M%D_%h%m%s" will yield
// the filename `task.log.20110501_123000` if the file is created on
// 01-May-2011 at 12:30:00 local time (assuming `enablePublishInLocalTime` was
// called).
//
///Log File Rotation
///-----------------
// A `ball::FileObserver2` may be configured to perform automatic rotation of
// log files based on simple file rotation conditions (or rules).
//
///File Rotation Conditions
/// - - - - - - - - - - - -
// Rotation rules may be established based on the size of the log file (i.e., a
// "rotation-on-size" rule), and a periodic time interval (i.e., a
// "rotation-on-time-interval" rule).  These rules are independently enabled by
// the `rotateOnSize` and `rotateOnTimeInterval` methods, respectively.  If
// both rules are in effect, log file rotation is performed whenever either
// rule applies.
//
///Rotated File Naming
///- - - - - - - - - -
// When a log file is rotated, a new filename is generated using the pattern
// supplied to `enableFileLogging`.  If the file having the new name does not
// exist, the current log file is closed, and the logging continues to the new
// file.
//
// If the file having the new name already exits, then the behavior of the file
// rotation is further controlled by the flag set with
// `suppressUniqueFileNameOnRotation`:
//
// * `suppressUniqueFileNameOnRotation(false)` (*default* behavior)
//   The current log filename is renamed by appending a timestamp in the form
//   ".%Y%M%D_%h%m%s" where the timestamp indicates when the file being
//   rotated was last opened (the time of either the last file rotation or the
//   last call to `enableFileLogging`, whichever is most recent).  As with the
//   timestamps of logged records, the timestamps appended to log filenames
//   upon rotation will be in UTC time or local time depending on the value
//   returned by `isPublishInLocalTimeEnabled`.
// * `suppressUniqueFileNameOnRotation(true)`
//   The logging continues to the *current* log file, effectively suppressing
//   log filename rotation.  This may happen when the log file pattern does
//   not contain %-escape sequences indicating a time period, or the rotation
//   interval is less than the time period encoded by %-escape sequences.  In
//   order to rotate log files in this mode, the log file pattern MUST contain
//   %-escape sequences that specify date and (optionally) time.  For example,
//   the log filename pattern "app_%Y%M%D.log" will produce a single log file
//   per calendar day (assuming, the rotation on time is enabled and the
//   rotation happens at least once a day).
//
// The two tables below illustrate the names of old and new log files when a
// file rotation occurs.  We assume that the log file is rotated on 2011-May-21
// at 12:29:59 local time and that the last rotation occurred at 12:30:00 on
// the previous day.  We further assume that `enablePublishInLocalTime` was
// called, so that all date and time elements are rendered in local time.
//
// The first table shows the name change (if any) of the (old) log file being
// rotated when the flag controlled by `suppressUniqueFileNameOnRotation`
// is set to `false`:
// ```
// Disabled: `suppressUniqueFileNameOnRotation`
//
// For brevity:
//     <TS1> = 20210520_123000
//     <TS2> = 20210521_122959 (aka next day, about the same time)
//
// +----------------+-----------------+----------------+----------------------
// | Pattern        | Filename Before | Filename After | Rotated Filename
// |                | Rotation        | Rotation       |
// +----------------+-----------------+----------------+----------------------
// | "a.log"        | a.log           | a.log          | a.log.<TS1>
// | "a.log.%T"     | a.log.<TS1>     | a.log.<TS2>    | a.log.<TS1>
// | "a.log.%Y%M"   | a.log.202105    | a.log.202105   | a.log.202105.<TS1>
// | "a.log.%Y%M%D" | a.log.20210520  | a.log.20110521 | a.log.20210520
// +----------------+-----------------+----------------+----------------------
// ```
// Note that upon rotation a timestamp was appended to the name of the rotated
// file when the log pattern does not contain %-escape sequences indicating a
// time period ("a.log"), or the rotation period (in our case, one day) is less
// than the time period encoded in the pattern (in case of "a.log.%Y%M" the
// period is one month).
//
// The next table shows the rotated name when the flag controlled by
// `suppressUniqueFileNameOnRotation` is set to `true`, and (possibly new) name
// of the (new) log file following rotation:
// ```
// Enabled: `suppressUniqueFileNameOnRotation`
//
// +----------------+-----------------+----------------+----------------------
// | Pattern        | Filename Before | Filename After | Rotated Filename
// |                | Rotation        | Rotation       |
// +----------------+-----------------+----------------+----------------------
// | "a.log"        | a.log           | a.log          | none
// | "a.log.%T"     | a.log.<TS1>     | a.log.<TS2>    | a.log.<TS1>
// | "a.log.%Y%M"   | a.log.202105    | a.log.202105   | none
// | "a.log.%Y%M%D" | a.log.20210520  | a.log.20110521 | a.log.20210520
// +----------------+-----------------+----------------+----------------------
// ```
// Note that the original filename is reused when the log pattern does not
// contain %-escape sequences indicating a time period ("a.log"), or the
// rotation period (in our case, one day) is less than the time period encoded
// in the pattern (in case of "a.log.%Y%M" the period is one month).
//
// Also note, that in any cases, when the log pattern includes "%T", or encodes
// a time period that coincides the rotation period (in case of "a.log.%Y%M%D"
// the period is one day), then a unique name on each rotation is produced with
// the (local) time at which file rotation occurred embedded in the filename.
//
///Thread Safety
///-------------
// All methods of `ball::FileObserver2` are thread-safe, and can be called
// concurrently by multiple threads.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example: Basic Usage
/// - - - - - - - - - -
// First, we create a `ball::LoggerManagerConfiguration` object, `lmConfig`,
// and set the logging "pass-through" level -- the level at which log records
// are published to registered observers -- to `DEBUG`:
// ```
// int main()
// {
//     ball::LoggerManagerConfiguration lmConfig;
//     lmConfig.setDefaultThresholdLevelsIfValid(ball::Severity::e_DEBUG);
// ```
// Next, create a `ball::LoggerManagerScopedGuard` object whose constructor
// takes the configuration object just created.  The guard will initialize the
// logger manager singleton on creation and destroy the singleton upon
// destruction.  This guarantees that any resources used by the logger manager
// will be properly released when they are not needed:
// ```
//     ball::LoggerManagerScopedGuard guard(lmConfig);
//     ball::LoggerManager& manager = ball::LoggerManager::singleton();
// ```
// Next, we create a `ball::FileObserver2` object and register it with the
// `ball` logging system;
// ```
//     bsl::shared_ptr<ball::FileObserver2> observer =
//                                    bsl::make_shared<ball::FileObserver2>();
// ```
// Next, we configure the log file rotation rules:
// ```
//     // Rotate the file when its size becomes greater than or equal to 128
//     // megabytes.
//     observer->rotateOnSize(1024 * 128);
//
//     // Rotate the file every 24 hours.
//     observer->rotateOnTimeInterval(bdlt::DatetimeInterval(1));
// ```
// Note that in this configuration the user may end up with multiple log files
// for a specific day (because of the rotation-on-size rule).
//
// Then, we enable logging to a file:
// ```
//     // Create and log records to a file named "/var/log/task/task.log".
//     observer->enableFileLogging("/var/log/task/task.log");
// ```
// Finally, we register the file observer with the logger manager.  Upon
// successful registration, the observer will start to receive log records via
// the `publish` method:
// ```
//     int rc = manager.registerObserver(observer, "default");
//     assert(0 == rc);
//     return 0;
// }
// ```

#include <balscm_version.h>

#include <ball_observer.h>
#include <ball_severity.h>

#include <bdls_fdstreambuf.h>

#include <bdlt_datetime.h>
#include <bdlt_datetimeinterval.h>

#include <bslma_allocator.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_nestedtraitdeclaration.h>

#include <bslmt_mutex.h>

#include <bsls_keyword.h>
#include <bsls_libraryfeatures.h>

#include <bsl_fstream.h>
#include <bsl_functional.h>
#include <bsl_iosfwd.h>
#include <bsl_memory.h>
#include <bsl_string.h>

#include <string>           // `std::string`, `std::pmr::string`

namespace BloombergLP {
namespace ball {

class Context;
class Record;

                          // ===================
                          // class FileObserver2
                          // ===================

/// This class implements the `Observer` protocol.  The `publish` method of
/// this class outputs the log records that it receives to a user-specified
/// file.  This class is thread-safe; different threads can operate on an
/// object concurrently.  This class is exception-neutral with no guarantee
/// of rollback.  In no event is memory leaked.
class FileObserver2 : public Observer {

  public:
    // PUBLIC TYPES

    /// `LogRecordFunctor` is an alias for the type of the functor used for
    /// formatting log records to a stream.
    typedef bsl::function<void(bsl::ostream&, const Record&)> LogRecordFunctor;

    /// `OnFileRotationCallback` is an alias for a user-supplied callback
    /// function that is invoked after the file observer attempts to rotate
    /// its log file.  The callback takes two arguments: (1) an integer
    /// status value where 0 indicates a new log file was successfully
    /// created and a non-zero value indicates an error occurred during
    /// rotation, and (2) a string that provides the name of the rotated log
    /// file if the rotation was successful.  E.g.:
    /// ```
    /// void onLogFileRotation(int                rotationStatus,
    ///                        const bsl::string& rotatedLogFileName);
    /// ```
    typedef bsl::function<void(int, const bsl::string&)>
                                                        OnFileRotationCallback;

  private:
    // DATA
    bdls::FdStreamBuf      d_logStreamBuf;             // stream buffer for
                                                       // file logging

    bsl::ostream           d_logOutStream;             // output stream for
                                                       // file logging (refers
                                                       // to `d_logStreamBuf`)

    bsl::string            d_logFilePattern;           // log filename pattern

    bsl::string            d_logFileName;              // current log filename

    bdlt::Datetime         d_logFileTimestampUtc;      // modification time of
                                                       // the log file when it
                                                       // was opened (or the
                                                       // creation time if the
                                                       // log file did not
                                                       // already exist)

    LogRecordFunctor       d_logFileFunctor;           // formatting functor
                                                       // used when writing to
                                                       // log file

    bool                   d_publishInLocalTime;       // `true` if timestamps
                                                       // of records are output
                                                       // in local time,
                                                       // otherwise UTC time

    bool                   d_suppressUniqueFileName;   // `false` if rotated
                                                       // log file has a unique
                                                       // name

    mutable bslmt::Mutex   d_mutex;                    // serialize operations

    int                    d_rotationSize;             // maximum log file size
                                                       // before rotation (in
                                                       // kilobytes)

    bdlt::Datetime         d_rotationReferenceTime;    // reference start time
                                                       // (time-zone determined
                                                       // by
                                                       // d_publishInLocalTime)
                                                       // for time-based
                                                       // rotation

    bdlt::DatetimeInterval d_rotationInterval;         // time interval between
                                                       // two time-based
                                                       // rotations

    bdlt::Datetime         d_nextRotationTimeUtc;      // next scheduled time
                                                       // for time-based
                                                       // rotation

    OnFileRotationCallback d_onRotationCb;             // user callback
                                                       // invoked following
                                                       // file rotation

    mutable bslmt::Mutex   d_rotationCbMutex;          // serialize access to
                                                       // 'd_onRotationCb';
                                                       // required because
                                                       // callback must be
                                                       // called with 'd_mutex'
                                                       // unlocked

  private:
    // NOT IMPLEMENTED
    FileObserver2(const FileObserver2&);
    FileObserver2& operator=(const FileObserver2&);

  private:
    // PRIVATE MANIPULATORS

    /// Write the specified log `record` to the specified output `stream`
    /// using the default record format of this file observer.
    void logRecordDefault(bsl::ostream& stream, const Record& record);

    /// Perform a log file rotation by closing the current log file of this
    /// file observer, renaming the closed log file if necessary, and
    /// opening a new log file.  Load, into the specified
    /// `rotatedLogFileName`, the name of the rotated log file.  Return 0 on
    /// success, a positive value if logging is not enabled, and a negative
    /// value otherwise.  The existing log file is renamed if the new log
    /// filename, as determined by the `logFilenamePattern` of the latest
    /// call to `enableFileLogging`, is the same as the old log filename.
    int rotateFile(bsl::string *rotatedLogFileName);

    /// Perform log file rotation if the specified `currentLogTimeUtc` is
    /// later than the scheduled rotation time of the current log file, or
    /// if the log file is larger than the allowable size, and if a rotation
    /// is performed, load into the specified `rotatedLogFileName` the name
    /// of the rotated file.  Return 0 if the log file is rotated
    /// successfully, a positive value if a rotation was determined to be
    /// unnecessary, and a negative value otherwise.  The rotation schedule
    /// and the allowable file size are set by the `rotateOnTimeInterval`
    /// and the `rotateOnSize` methods, respectively.  The behavior is
    /// undefined unless the caller acquired the lock for this object.
    int rotateIfNecessary(bsl::string           *rotatedLogFileName,
                          const bdlt::Datetime&  currentLogTimeUtc);

    // PRIVATE ACCESSORS

    /// Return `true` if file logging is enabled for this file observer, and
    /// `false` otherwise.  Load the specified `result` with the name of the
    /// current log file if file logging is enabled, and leave `result`
    /// unmodified otherwise.
    template <class t_STRING>
    bool isFileLoggingEnabledImpl(t_STRING *result) const;

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(FileObserver2, bslma::UsesBslmaAllocator);

    // CREATORS

    /// Create a file observer with file logging initially disabled.
    /// Optionally specify a `basicAllocator` used to supply memory.  If
    /// `basicAllocator` is 0, the currently installed default allocator is
    /// used.  Note that `isPublishInLocalTimeEnabled` returns `false`
    /// following construction indicating that the timestamp attribute of
    /// published records will be written in UTC time (see
    /// `enablePublishInLocalTime`).  Also note that a default record format
    /// is in effect for file logging (see `setLogFileFunctor`).
    explicit FileObserver2(bslma::Allocator *basicAllocator = 0);

    /// Close the log file of this file observer if file logging is enabled,
    /// and destroy this file observer.
    ~FileObserver2() BSLS_KEYWORD_OVERRIDE;

    // MANIPULATORS

    /// Disable file logging for this file observer.  This method has no
    /// effect if file logging is not enabled.  Note that records
    /// subsequently received through the `publish` method will be dropped
    /// until file logging is re-enabled.
    void disableFileLogging();

    /// Disable log file rotation based on a periodic time interval for this
    /// file observer.  This method has no effect if
    /// rotation-on-time-interval is not enabled.
    ///
    /// @DEPRECATED: Use `disableTimeIntervalRotation` instead.
    void disableLifetimeRotation();

    /// Disable publishing of the timestamp attribute of records in local
    /// time by this file observer; henceforth, timestamps will be in UTC
    /// time.  This method has no effect if publishing in local time is not
    /// enabled.  Note that this method also affects log filenames (see {Log
    /// Filename Patterns}).
    void disablePublishInLocalTime();

    /// Disable log file rotation based on log file size for this file
    /// observer.  This method has no effect if rotation-on-size is not
    /// enabled.
    void disableSizeRotation();

    /// Disable log file rotation based on a periodic time interval for this
    /// file observer.  This method has no effect if
    /// rotation-on-time-interval is not enabled.
    void disableTimeIntervalRotation();

    /// Enable logging of all records published to this file observer to a
    /// file whose name is derived from the specified `logFilenamePattern`.
    /// Return 0 on success, a positive value if file logging is already
    /// enabled (with no effect), and a negative value otherwise.  The
    /// basename of `logFilenamePattern` may contain `%`-escape sequences
    /// that are interpreted as follows:
    /// ```
    ///  %Y - current year   (4 digits with leading zeros)
    ///  %M - current month  (2 digits with leading zeros)
    ///  %D - current day    (2 digits with leading zeros)
    ///  %h - current hour   (2 digits with leading zeros)
    ///  %m - current minute (2 digits with leading zeros)
    ///  %s - current second (2 digits with leading zeros)
    ///  %T - current datetime, equivalent to "%Y%M%D_%h%m%s"
    ///  %p - process ID
    /// ```
    /// Each time a log file is opened by this file observer (upon a
    /// successful call to this method and following each log file
    /// rotation), the name of the new log file is derived from
    /// `logFilenamePattern` by interpreting the above recognized `%`-escape
    /// sequences.  If `isPublishInLocalTimeEnabled` returns `true`, the
    /// `%`-escape sequences related to time will be substituted with local
    /// time values, and UTC time values otherwise.  See {Log Filename
    /// Patterns}.
    int enableFileLogging(const char *logFilenamePattern);

    /// Enable logging of all records published to this file observer to a
    /// file whose name is derived from the specified `logFilenamePattern`
    /// and append a timestamp to the log filename if the specified
    /// `appendTimestampFlag` is `true`.  Return 0 on success, a positive
    /// value if file logging is already enabled (with no effect), and a
    /// negative value otherwise.  If the `appendTimestampFlag` is `true`
    /// and `logFilenamePattern` does not contain any `%`-escape sequences,
    /// this method behaves as if ".%T" is appended to `logFilenamePattern`.
    ///
    /// @DEPRECATED: Use `enableFileLogging(logFilenamePattern)` instead
    /// (use the ".%T" pattern to replicate `true == appendTimestampFlag`
    /// behavior).
    int enableFileLogging(const char *logFilenamePattern,
                          bool        appendTimestampFlag);

    /// Enable publishing of the timestamp attribute of records in local
    /// time by this file observer.  This method has no effect if publishing
    /// in local time is already enabled.  Note that this method also
    /// affects log filenames (see {Log Filename Patterns}).
    void enablePublishInLocalTime();

    /// Process the specified log `record` having the specified publishing
    /// `context` by writing `record` and `context` to the current log file
    /// if file logging is enabled for this file observer.  The method has
    /// no effect if file logging is not enabled, in which case `record` is
    /// dropped.
    ///
    /// @DEPRECATED: Do not use.
    void publish(const Record&  record,
                 const Context& context) BSLS_KEYWORD_OVERRIDE;

    /// Process the record referenced by the specified `record` shared
    /// pointer having the specified publishing `context` by writing the
    /// record and `context` to the current log file if file logging is
    /// enabled for this file observer.  The method has no effect if file
    /// logging is not enabled, in which case `record` is dropped.
    void publish(const bsl::shared_ptr<const Record>& record,
                 const Context&                       context)
                                                         BSLS_KEYWORD_OVERRIDE;

    /// Discard any shared references to `Record` objects that were supplied
    /// to the `publish` method, and are held by this observer.  Note that
    /// this operation should be called if resources underlying the
    /// previously provided shared pointers must be released.
    void releaseRecords() BSLS_KEYWORD_OVERRIDE;

    /// Forcefully perform a log file rotation by this file observer.  Close
    /// the current log file, rename the log file if necessary, and open a
    /// new log file.  This method has no effect if file logging is not
    /// enabled.  See {Rotated File Naming} for details on filenames of
    /// rotated log files.
    void forceRotation();

    /// Set this file observer to perform log file rotation when the size of
    /// the file exceeds the specified `size` (in kilobytes).  This rule
    /// replaces any rotation-on-size rule currently in effect.  The
    /// behavior is undefined unless `size > 0`.
    void rotateOnSize(int size);

    /// Set this file observer to perform a periodic log file rotation at
    /// multiples of the specified `timeInterval`.  This rule replaces any
    /// rotation-on-time-interval rule currently in effect.  The behavior is
    /// undefined unless `0 < timeInterval.totalMilliseconds()`.
    ///
    /// @DEPRECATED: Use `rotateOnTimeInterval` instead.
    void rotateOnLifetime(const bdlt::DatetimeInterval& timeInterval);

    /// Set this file observer to perform a periodic log file rotation at
    /// multiples of the specified `interval`.  Optionally specify a
    /// `startTime` indicating the datetime to use as the starting point for
    /// computing the periodic rotation schedule.  If
    /// `isPublishInLocalTimeEnabled` is `true`, the `startTime` is
    /// interpreted as local time, and as a UTC time otherwise.  If
    /// `startTime` is not specified, the current time is used.  This rule
    /// replaces any rotation-on-time-interval rule currently in effect.
    /// The behavior is undefined unless `0 < interval.totalMilliseconds()`.
    /// Note that `startTime` may be a fixed time in the past; e.g., a
    /// reference time of `bdlt::Datetime(1, 1, 1)` and an interval of 24
    /// hours would configure a periodic rotation at midnight each day.
    void rotateOnTimeInterval(const bdlt::DatetimeInterval& interval);
    void rotateOnTimeInterval(const bdlt::DatetimeInterval& interval,
                              const bdlt::Datetime&         startTime);

    /// Set the formatting functor used when writing records to the log file
    /// of this file observer to the specified `logFileFunctor`.  Note that
    /// a default format ("\n%d %p:%t %s %f:%l %c %m %u\n") is in effect
    /// until this method is called (see `ball_recordstringformatter`).
    /// Also note that the observer emits newline characters at the
    /// beginning and at the end of a log record by default, so the user
    /// needs to add them explicitly to the format string to preserve this
    /// behavior.
    void setLogFileFunctor(const LogRecordFunctor& logFileFunctor);


    /// Set the specified `onRotationCallback` to be invoked after each time
    /// this file observer attempts to perform a log file rotation.  The
    /// behavior is undefined if the supplied function calls either
    /// `setOnFileRotationCallback`, `forceRotation`, or `publish` on this
    /// file observer (i.e., the supplied callback should *not* attempt to
    /// write to the `ball` log).
    void setOnFileRotationCallback(
                             const OnFileRotationCallback& onRotationCallback);

    /// Suppress generating a unique log file name upon rotation if the
    /// specified `suppress` is `true`, and generate a unique filename
    /// otherwise.  See {Rotated File Naming} for details.
    void suppressUniqueFileNameOnRotation(bool suppress);

    // ACCESSORS

    /// Return `true` if file logging is enabled for this file observer, and
    /// `false` otherwise.  Load the optionally specified `result` with the
    /// name of the current log file if file logging is enabled, and leave
    /// `result` unmodified otherwise.  Note that records received through
    /// the `publish` method of this file observer will be dropped when this
    /// method returns `false`.
    bool isFileLoggingEnabled() const;
    bool isFileLoggingEnabled(bsl::string *result) const;
    bool isFileLoggingEnabled(std::string *result) const;

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR_STRING
    /// Return `true` if file logging is enabled for this file observer, and
    /// `false` otherwise.  Load the specified `result` with the name of the
    /// current log file if file logging is enabled, and leave `result`
    /// unmodified otherwise.
    bool isFileLoggingEnabled(std::pmr::string *result) const;
#endif  // BSLS_LIBRARYFEATURES_HAS_CPP17_PMR_STRING

    /// Return `true` if this file observer writes the timestamp attribute
    /// of records that it publishes in local time, and `false` otherwise
    /// (in which case timestamps are written in UTC time).  Note that the
    /// value returned by this method also affects log filenames (see {Log
    /// Filename Patterns}).
    bool isPublishInLocalTimeEnabled() const;

    /// Return `true` if the log filename uniqueness check on rotation is
    /// suppressed, and false otherwise.
    bool isSuppressUniqueFileNameOnRotation() const;

    /// Return the lifetime of the log file that will trigger a file
    /// rotation by this file observer if rotation-on-lifetime is in effect,
    /// and a 0 time interval otherwise.
    bdlt::DatetimeInterval rotationLifetime() const;

    /// Return the size (in kilobytes) of the log file that will trigger a
    /// file rotation by this file observer if rotation-on-size is in
    /// effect, and 0 otherwise.
    int rotationSize() const;

    /// Return the difference between the local time and UTC time in effect
    /// when this file observer was constructed.  Note that this value
    /// remains unchanged during the lifetime of this object and therefore
    /// may become incorrect when the difference between the local time and
    /// UTC time changes (e.g., when transitioning into or out of daylight
    /// savings time).
    ///
    /// @DEPRECATED: Use `bdlt::LocalTimeOffset` instead.
    bdlt::DatetimeInterval localTimeOffset() const;
};

// ============================================================================
//                              INLINE DEFINITIONS
// ============================================================================

                          // -------------------
                          // class FileObserver2
                          // -------------------

// MANIPULATORS
inline
void FileObserver2::publish(const bsl::shared_ptr<const Record>& record,
                            const Context&                       context)
{
    publish(*record, context);
}

inline
void FileObserver2::releaseRecords()
{
}

}  // close package namespace
}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2015 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
